﻿/// 先加载 characterelments.assetbundle 获取角色信息，包括肢体与对应材质的映射关系，
using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;


public class CharacterCtrl
{
    GameObject _root;
    Animation _ani;

    public GameObject root { get { return _root; } }

    public CharacterCtrl(GameObject root)
    {
        _root = root;
        _ani = _root.GetComponent<Animation>();
    }

    public void PlayInit()
    {
        _ani.Play("idle1");
        _ani["idle1"].wrapMode = WrapMode.Loop;

        if (_ani["walkin"] != null)
        {
            _ani["walkin"].layer = 1;
            _ani["walkin"].weight = 1;
            _ani.CrossFade("walkin", 0.8f);
        }
    }

    public void PlayAni(string name)
    {
        _ani.Play(name);
    }
}


// 角色各肢体部位信息
public class CharacterPartInfo
{
    public string name; // 部位名称
    public List<CharacterElement> elems = new List<CharacterElement>(); // 部位材质信息
}

// 角色信息
public class CharacterInfo
{
    public string name; // 角色名称
    public Dictionary<string, CharacterPartInfo> partInfo = new Dictionary<string, CharacterPartInfo>();

    public CharacterPartInfo AddPart(string partName, CharacterElement elem)
    {
        CharacterPartInfo part;
        if (!partInfo.TryGetValue(partName, out part))
        {
            part = new CharacterPartInfo();
            part.name = partName;
            partInfo.Add(partName, part);
        }

        part.elems.Add(elem);
        return part;
    }
}

// 当前角色信息
public class CurrentCharacter
{
    public string chName;
    public Dictionary<string, CharacterElement> partDict = new Dictionary<string, CharacterElement>();
}

public class CharacterGenerator
{
    static CharacterGenerator _inst;
    public static CharacterGenerator inst
    {
        get
        {
            if (_inst == null)
                _inst = new CharacterGenerator();
            return _inst;
        }
    }

    CharacterABMgr _bundleMgr = new CharacterABMgr();
    Dictionary<string, CharacterInfo> _chDict = new Dictionary<string, CharacterInfo>();
    CurrentCharacter _current = new CurrentCharacter();

    CharacterInfo AddCharacter(string chName)
    {
        CharacterInfo ch;
        if (!_chDict.TryGetValue(chName, out ch))
        {
            ch = new CharacterInfo();
            _chDict.Add(chName, ch);
            return ch;
        }
        return ch;
    }

    void SetCharacter(string chName)
    {
        _current.chName = chName;
        foreach (var part in _chDict[chName].partInfo)
        {
            var elem = part.Value.elems[Random.Range(0, part.Value.elems.Count - 1)];
            _current.partDict[part.Key] = elem;
        }
    }

    void InitCharacter()
    {
        string chName = string.Empty;
        foreach (var one in _chDict)
            chName = one.Key;

        if (string.IsNullOrEmpty(chName))
            throw new Exception("no data for init character");
        SetCharacter(chName);
    }

    public void Init()
    {
        var dbBundleName = "characterelements.assetbundle";
        var dbAssetName = "characterelements.asset";
        var ceh = _bundleMgr.LoadAsset<CharacterElementHolder>(dbBundleName, dbAssetName);
        foreach (var one in ceh.content)
        {
            // 根据肢体 bundle 的名称解析出角色，角色的肢体部位
            var strArr = one.bundleName.Split('_');
            if (strArr.Length != 2)
                throw new Exception(string.Format("invalid part bundle name:{0}", one.bundleName));
            var chName = strArr[0];
            var strArr2 = strArr[1].Split('-');
            if (strArr2.Length > 2)
                throw new Exception(string.Format("invalid part bundle name:{0}", one.bundleName));
            else if (strArr2.Length == 1)
                strArr2 = strArr[1].Split('.');
            var partName = strArr2[0];

            var chInfo = AddCharacter(chName);
            chInfo.AddPart(partName, one);
        }

        InitCharacter();
    }

    public CharacterCtrl ShowCharacter(CharacterCtrl ctrl)
    {
        GameObject root;
        if (ctrl != null)
        {
            root = ctrl.root;
        }
        else
        {
            var baseBundleName = string.Format("{0}_characterbase.assetbundle", _current.chName);
            var baseAssetName = "characterbase.prefab";
            var baseGo = _bundleMgr.LoadAsset<GameObject>(baseBundleName, baseAssetName);
            root = Object.Instantiate<GameObject>(baseGo, Vector3.zero, Quaternion.identity);
            root.name = _current.chName;
        }

        var combineInsts = new List<CombineInstance>();
        var mats = new List<Material>();
        var bones = new List<Transform>();
        var joints = root.GetComponentsInChildren<Transform>();

        foreach (var part in _current.partDict)
        {
            var partBundleName = part.Value.bundleName;
            var partRenderAssetName = string.Format("renderobject.prefab");
            var partGo = _bundleMgr.LoadAsset<GameObject>(partBundleName, partRenderAssetName);

            var partBoneNamesAssetName = string.Format("bonenames.asset");
            var partBoneNames = _bundleMgr.LoadAsset<StringHolder>(partBundleName, partBoneNamesAssetName);

            var partMatAssetName = part.Value.materialName;
            var partMat = _bundleMgr.LoadAsset<Material>(partBundleName, partMatAssetName);

            var go = Object.Instantiate<GameObject>(partGo);
            var smr = go.GetComponent<SkinnedMeshRenderer>();
            smr.sharedMaterial = partMat;

            mats.AddRange(smr.sharedMaterials);

            for (var i = 0; i < smr.sharedMesh.subMeshCount; i++)
            {
                var ci = new CombineInstance();
                ci.subMeshIndex = i;
                ci.mesh = smr.sharedMesh;
                combineInsts.Add(ci);
            }

            foreach (var bn in partBoneNames.content)
            {
                foreach (var j in joints)
                {
                    if (j.name == bn)
                    {
                        bones.Add(j);
                        break;
                    }
                }
            }

            Object.Destroy(go);
        }
        // 渲染参数
        var rootSmr = root.GetComponent<SkinnedMeshRenderer>();
        rootSmr.sharedMesh = new Mesh();
        rootSmr.sharedMesh.CombineMeshes(combineInsts.ToArray(), false, false);
        rootSmr.bones = bones.ToArray();
        rootSmr.sharedMaterials = mats.ToArray();
        // 其他选项
        rootSmr.updateWhenOffscreen = true;

        if (ctrl == null)
            ctrl = new CharacterCtrl(root);
        return ctrl;
    }

    public CharacterCtrl ChangeCharacter(CharacterCtrl ctrl, bool next)
    {
        var col = _chDict.Keys;
        var arr = new string[col.Count];
        _chDict.Keys.CopyTo(arr, 0);

        string chName = string.Empty;
        for (var i = 0; i < arr.Length; i++)
        {
            if (arr[i] != _current.chName)
                continue;
            if (next)
                chName = i < arr.Length - 1 ? arr[i + 1] : arr[0];
            else
                chName = i > 0 ? arr[i - 1] : arr[arr.Length - 1];
            break;
        }

        if (!string.IsNullOrEmpty(chName))
        {
            SetCharacter(chName);
            Object.Destroy(ctrl.root);
            ctrl = ShowCharacter(null);
        }
        return ctrl;
    }

    public void ChangeElement(CharacterCtrl ctrl, string partName, bool next)
    {
        var part = _current.partDict[partName];
        var elems = _chDict[_current.chName].partInfo[partName].elems;
        for (var i = 0; i < elems.Count; i++)
        {
            if (elems[i] != part)
                continue;
            if (next)
                part = i < elems.Count - 1 ? elems[i + 1] : elems[0];
            else
                part = i > 0 ? elems[i - 1] : elems[elems.Count - 1];
            break;
        }

        _current.partDict[partName] = part;
        ShowCharacter(ctrl);
    }
}
